package com.bjpowernode.mvc.service.impl;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.bjpowernode.common.client.RealnameUtil;
import com.bjpowernode.common.client.RedisClient;
import com.bjpowernode.common.constant.AppConstants;
import com.bjpowernode.common.constant.RedisKey;
import com.bjpowernode.common.enums.RespCode;
import com.bjpowernode.db.domain.*;
import com.bjpowernode.db.mapper.*;
import com.bjpowernode.db.result.UserAccountResult;
import com.bjpowernode.mvc.exception.BizException;
import com.bjpowernode.mvc.model.dto.UserDTO;
import com.bjpowernode.mvc.model.dto.UserTokenDTO;
import com.bjpowernode.mvc.service.inter.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {

    private final SysUserMapper userMapper;
    private final SysAccountMapper sysAccountMapper;
    private final AccountDetailMapper accountDetailMapper;
    private final LoginDetailMapper loginDetailMapper;

    private final RedisClient redisClient;
    private final RealnameUtil realnameUtil;

    /**
     * 根据手机号查询用户
     *
     * @param phone
     * @return
     */
    @Override
    public SysUser queryByPhone(String phone) {
        SysUser sysUser = userMapper.selectOne(new QueryWrapper<SysUser>().eq("phone", phone));

        return sysUser;
    }

    /**
     * 检验短信验证码是否有效
     *
     * @param phone 手机号
     * @param code  验证码
     * @return 验证结果为true说明验证码有效
     */

    @Override
    public boolean checkSmsCode(String action, String phone, String code) {

        //1.取Redis中的key值
        String key = "";
        //注册
        if (AppConstants.ACTION_REGISTER.equals(action)) {
            key = RedisKey.SMS_CODE_REGISTER + phone + ":" + code;
        } else if (AppConstants.ACTION_LOGIN.equals(action)) {
            key = RedisKey.SMS_CODE_LOGIN + phone + ":" + code;
        }

        //2.跟Redis中的code作比对
        String queryCode = redisClient.getVal(key);

        return code.equals(queryCode);
    }

    /**
     * 注册用户  同步操作synchronized
     *
     * @param userDTO
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public synchronized boolean registerUser(UserDTO userDTO) {

        //1.检查手机号是否注册过
        SysUser queryUser = queryByPhone(userDTO.getPhone());
        if (queryUser != null) {
            return false;
        }

        //2.注册用户
        String salt = IdUtil.simpleUUID();
        String newPassword = DigestUtil.md5Hex(userDTO.getPassword() + salt);

        SysUser sysUser = new SysUser();
        sysUser.setPhone(userDTO.getPhone());
        sysUser.setLoginPassword(newPassword);
        sysUser.setAddTime(new Date());
        sysUser.setSaltPassword(salt);
        userMapper.insert(sysUser);

        //3.注册资金账号
        SysAccount sysAccount = new SysAccount();
        //在mybatis-plus中，如果id是自动增长的，则我们可以在后续取出id这个值
        sysAccount.setUid(sysUser.getId());
        sysAccount.setAvailableMoney(new BigDecimal("88"));
        sysAccountMapper.insert(sysAccount);

        //4.记录账号流水
        AccountDetail accountDetail = new AccountDetail();
        accountDetail.setUid(sysUser.getId());
        accountDetail.setAction(AppConstants.OPERATION_ADD);
        accountDetail.setActionTime(new Date());
        accountDetail.setMoney(new BigDecimal("88"));
        accountDetail.setMemo("注册新用户，体验金：88.0");
        accountDetailMapper.insert(accountDetail);
        return true;
    }

    /**
     * 登录功能
     *
     * @param phone
     * @param password
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysUser userLogin(String phone, String password, String ip) {
        //1、查询用户
        SysUser sysUser = queryByPhone(phone);
        if (sysUser == null) {
            throw new BizException(RespCode.PHONE_USER_NOT_EXITS);
        }
        //2、比对密码
        //获取盐值salt，进行密码的二次加密(MD5)
        String newPassword = DigestUtil.md5Hex(password + sysUser.getSaltPassword());

        if (!sysUser.getLoginPassword().equals(newPassword)) {
            //数据库中的密码与前端传入的密码不匹配
            throw new BizException(RespCode.USER_PASSWORD_ERR);
        }
        //用户登录成功，记录用户登录的信息
        //1.更新用户最近的登录时间
        UpdateWrapper<SysUser> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set("last_login_time", new Date());
        updateWrapper.eq("id", sysUser.getId());
        //update u_user set last_login_time = now() where id = xxxx
        //第一个是更新实体、第二个是更新条件
        //如果在更新条件中把所有的更新都封装好了，那么实体可以为null
        int result = userMapper.update(null, updateWrapper);

        //方案2：更新所有非空字段,比较浪费资源
        //sysUser.setLastLoginTime(new Date());
        //userMapper.updateById(sysUser);

        if (result < 1) {
            throw new RuntimeException("更新最近登录时间失败");
        }

        //2.记录用户登录的信息
        LoginDetail loginDetail = new LoginDetail();
        loginDetail.setLoginTime(new Date());
        loginDetail.setLoginIp(ip);
        loginDetail.setUid(sysUser.getId());
        loginDetail.setPhone(phone);

        loginDetailMapper.insert(loginDetail);
        return sysUser;
    }

    /**
     * 生成token    向Redis中存储token
     *
     * @param sysUser
     * @return StrUtil.isEmpty(sysUser.getName ()) ? "-" : sysUser.getName()
     */
    @Override
    public String createToken(SysUser sysUser) {
        String token = UUID.randomUUID().toString().replaceAll("-", "");
        //存储token ，使用hash类型。
        String key = RedisKey.TOKEN_ACCESS + token.toUpperCase();
        Map<String, String> data = new HashMap<>();
        data.put("uid", String.valueOf(sysUser.getId()));
        data.put("token", token);
        data.put("name", StrUtil.isEmpty(sysUser.getName()) ? "-" : sysUser.getName());
        data.put("phone", sysUser.getPhone());
        //存储token到redis中(hash类型)   60分钟
        Boolean save = redisClient.addHash(key, data, 60, TimeUnit.MINUTES);
        if (save) {
            return token;
        }
        return null;
    }

    /**
     * 实名认证
     *
     * @param uid
     * @param name
     * @param idCard
     * @return
     */
    @Override
    public boolean realname(Integer uid, String name, String idCard, String token) {

        //做实名认证
        boolean idCardAuth = false;
        String json = realnameUtil.execRealnameApi(name, idCard);
        if (StrUtil.isNotBlank(json)) {
            //JSONUtil.parseObj()方法：将json数据转为JSONObject对象
            JSONObject result = JSONUtil.parseObj(json);
            Integer code = result.getInt("code", 0);
            if (code == 200) {
                //请求成功,获取结果JSON体中的result的值
                Integer anInt = result.getJSONObject("data").getInt("result", -1);
                //0表示一致，即认证成功
                if (anInt == 0) {
                    idCardAuth = true;
                }
            }
        }

        if (idCardAuth) {
            //1、更新数据库，name、idcard
            UpdateWrapper<SysUser> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("id", uid);
            updateWrapper.set("name", name);
            updateWrapper.set("id_card", idCard);
            int update = userMapper.update(null, updateWrapper);
            if (update < 1) {
                //更新用户信息失败
                throw new RuntimeException("更新用户信息失败");
            }

            //2、更新Redis中的用户数据
            String key = RedisKey.TOKEN_ACCESS + token.toUpperCase();
            redisClient.updateField(key, "name", name);
        }
        return idCardAuth;
    }


    /**
     * 根据主键用户id查找用户
     *
     * @param uid
     * @return
     */
    @Override
    public SysUser queryById(Integer uid) {
        return userMapper.selectById(uid);
    }

    /**
     * 查询用户详情
     *
     * @param uid
     * @return
     */

    @Override
    public UserAccountResult queryUserAccountByUid(Integer uid) {
        return userMapper.selectUserAccountByUid(uid);
    }

    /**
     * 根据token获取用户的登录信息
     *
     * @param token
     */
    @Override
    public UserTokenDTO getLoginInfo(String token) {
        //2、通过Redis获取用户数据
        Map<Object, Object> userMap = redisClient.getAllHashField(RedisKey.TOKEN_ACCESS + token.toUpperCase());
        UserTokenDTO userTokenDTO = new UserTokenDTO();
        userTokenDTO.setName((String) userMap.get("name"));
        userTokenDTO.setPhone((String) userMap.get("phone"));
        userTokenDTO.setToken((String) userMap.get("token"));
        userTokenDTO.setUid(Integer.valueOf((String) userMap.get("uid")));
        return userTokenDTO;
    }

    /**
     * 用户退出登录
     *
     * @param token
     * @return
     */
    @Override
    public boolean userLogout(String token) {
        String key = RedisKey.TOKEN_ACCESS + token.toUpperCase();
        return redisClient.deleteKey(key);
    }
}
